/*! 
 * jquery.ux.blackbox - v 1.0.0 - http://jquery-ux.com/
 * Copyright (c) 2010 Michael Helgeson, Three Dub Media
 * Open Source MIT License - http://jquery-ux.com/license 
 */
// Created: 2007-03-22 
// Updated: 2010-01-22
// REQUIRES: jquery 1.4+, jquery.ux

;(function( $ ){ // confine scope

$.ux( "blackbox", { // Register the PLUGIN... and public methods...
	version: "1.0.0",
	expose: "show hide size align", // + enable disable destroy
	defaults: {
		// specify overlay contennt...
		html: '<h1>Please wait...</h1>', // {string|function|element|jquery} box content to be appended
		once: true, // {Boolean} true means only append the html option once, else on every "show" call
		empty: false, // {Boolean} true means empty the "box" content on every "hide" call
		isolate: false, // {Boolean} true means only this instance may be visible at a time ("hide" others onShow)
		// configure overlay bg appearance...
		opacity: null, // {Float}
		background: null, // {String} 
		// box size & position...
		height: null, // {String|Number|NULL} pixels, em, percent (of the viewport)
		width: null, // {String|Number|NULL} pixels, em, percent (of the viewport)
		top: null, // {String|Number|NULL}
		left: null, // {String|Number|NULL}
		// interaction behavior
		showEvent: "click", // {String|NULL} which event on the element triggers "show", false to prevent
		hideEvent: "click", // {String|NULL} which event on the bg div triggers "hide", false to prevent
		classroot: "ux-blackbox" 
		// ux-blackbox-bg
		// ux-blackbox-box
		// ux-blackbox-wrap
		},
	create: function( elem, opts ){
		// store the arguments, extend instance...
		$.extend( this, { elem:elem, opts:opts, $elem:$(elem), html:opts.html });	
		// bind the element "show" event
		if ( opts.showEvent ) 
			this.$elem.bind( opts.showEvent, $.proxy( this, "show") );	
		// store size and position from options...
		this.size( opts.width, opts.height ); 
		this.align( opts.left, opts.top ); 
		},
	destroy: function(){
		this.hide(); 
		if ( this.opts.showEvent ) // unbind the element "show" event
			this.$elem.unbind( this.opts.showEvent, $.proxy( this, "show") );
		if ( this.$wrap ) // remove the markup
			this.$wrap.remove().empty(); 
		},
	methods: {
		initialize: function(){ // generate elements 
			this.$wrap = $('<div />').css({ 
					position:'absolute', 
					top:0, left:0 
					})
				.addClass( this.classify("wrap") )
				.hide();
			this.$bg = $('<div />').css({ 
					position:'absolute', 
					top:0, left:0,
					background: this.opts.background,
					opacity: this.opts.opacity
					})			
				.addClass( this.classify("bg") )
				.appendTo( this.$wrap );
			this.$box = $('<div />').css({ 
					position:'absolute' 
					})
				.addClass( this.classify("box") )
				.appendTo( this.$wrap );
			try { this.$div = $('<div />').bgiframe(); } catch( ex ){ } // try "bgiframe" 
			if ( this.$div && this.$div.children().length ){ // IE6 bgiframe fix...
				this.$div.css('position','absolute').prependTo( this.$wrap );
				this.$bg = this.$bg.add( this.$div );
				}
			if ( this.opts.hideEvent ) // bind the bg close event
				this.$bg.bind( this.opts.hideEvent, $.proxy( this, "hide") );
			this.X = this.Y = this.W = this.H = 0;
			},
		show: function( html ){ 
			if ( this.disabled || isolated ) return false;
			if ( !this.$wrap ) this.initialize();	
			if ( this.callback("beforeshow") ){ // "blackbox:before:show"
				if ( html = ( html && !html.stopPropagation ) ? html : this.html ){ // fill box content
					if ( $.isFunction( html ) ) 
						html = html.call( this.elem, this.$box );
					if ( html ) this.$box.append( html ); // insert any html
					if ( this.opts.once ) this.html = false; // clear the html
					}
				if ( this.opts.isolate ) { // isolate this overlay
					$.ux("blackbox.hide"); // close everything else that is open
					if ( opened ) return; // something else is still open
					else isolated = true; // everything is closed 
					}
				this.$wrap.css('z-index', $.blackbox.z++ ).appendTo( $body );
				this.isOpen = true;	
				opened += 1;
				_resize( true );
				this.callback( "show" ); // "blackbox:show"
				}
			return false;
			},
		hide: function(){
			if ( !this.isOpen || !this.callback("beforehide") ) return;
			if ( this.opts.isolate ) isolated = false;
			if ( this.opts.empty ) this.$box.empty();
			$body[0].removeChild( this.$wrap.hide()[0] );
			this.isOpen = false;
			opened -= 1;
			_pagesize(); 
			this.callback( "hide" ); // "blackbox:hide"
			},
		callback: function( fn ){ // publish custom event, return result
			return !this.publish( fn, this.$box ).isDefaultPrevented();
			},
		align: function( left, top ){ 
			left = this.left = left != null ? left : this.left;
			top = this.top = top != null ? top : this.top;
			if ( !this.isOpen ) return; // break			
			if ( left == null ){ // horizontal center	
				if ( this.W > viewport_width ){ // horizontal overflow
					left = ( scroll_x < 0 && this.X + this.W < scroll_left + viewport_width ) ? 
						scroll_left + viewport_width - this.W : // right edge
						( scroll_x > 0 && this.X > scroll_left ) ? 
						scroll_left : Math.min( this.X, scroll_left ); // left edge 
					}
				else left = Math.round( ( viewport_width  - this.W )/2 ) + scroll_left; // viewport center
				left = Math.min( Math.max( left, 0 ), mask_width - this.W ); // page limits
				}	
			if ( top == null ){ // vertical center	
				if ( this.H > viewport_height ){ // vertical overflow
					top = ( scroll_y < 0 && this.Y + this.H < scroll_top + viewport_height ) ? 
						scroll_top + viewport_height - this.H : // bottom edge
						( scroll_y > 0 && this.Y > scroll_top ) ? 
						scroll_top : Math.min( this.Y, scroll_top ); // top edge
					}
				else top = Math.round( ( viewport_height - this.H )/2 ) + scroll_top; // viewport center
				top = Math.min( Math.max( top, 0 ), mask_height - this.H ); // page limits
				}				
			this.$box.css({ top:( this.Y = top ), left:( this.X = left ) }); // position box	
			this.callback( "align" ); // "blackbox:align"
			// has viewport changed (+/- scrollbars) without resize?
			if ( viewport_width != $window.width() || viewport_height != $window.height() ) 
				_resize();
			},
		fix: function( attr ){ // fixes percentage layout in IE6
			var dimension = this[ attr ] || "", // height or width
			viewport = attr=="width" ? viewport_width : viewport_height;			
			return ( this.$bg.length > 1 && ( dimension +"" ).indexOf("%") > 0 ) ? 
				viewport * ( parseFloat( dimension.replace('%',"") )/100 ) : dimension;
			},
		size: function( width, height ){ 
			this.width = width != null ? width : this.width;
			this.height = height != null ? height : this.height;
			if ( !this.isOpen ) return; // break		
			// match the viewport, gives the box dimension
			this.$wrap.width( viewport_width ).height( viewport_height ).show();	
			// figure out box width	and height
			this.W = this.$box.width( this.fix("width") ).outerWidth();
			this.H = this.$box.height( this.fix("height") ).outerHeight();
			_pagesize(); // adjust page size
			// cover the entire page
			this.$bg.width( mask_width ).height( mask_height );	
			this.callback( "size" ); // "blackbox:size"
			this.align(); // set position
			}
		}
	});
// static base z-index for overlays
$.blackbox.z = 999;
/*******************************************************************************************/
// private variables and static functions 
var $window, $body, opened = 0, isolated = false,
viewport_height = 0, viewport_width = 0, 
scroll_top = 0, scroll_left = 0,
scroll_y = 0, scroll_x = 0,
page_height = 0, page_width = 0,
body_height = 0, body_width = 0,
mask_height = 0, mask_width = 0;
// adjust the page dimensions...
function _pagesize(){ 
	var width = body_width, height = body_height;
	$.each( $.ux.plugins.blackbox.cache, function( id, obj ){
		if ( !obj.instance.isOpen ) return;
		width = Math.max( obj.instance.W, width );
		height = Math.max( obj.instance.H, height );
		}); 
	if ( width != page_width ) // set/reset page width
		$body.width( ( page_width = width ) > body_width ? page_width : "" );
	if ( height != page_height ) // set/reset page height
		$body.height( ( page_height = height ) > body_height ? page_height : "" );
	// update the actual background mask dimensions...
	mask_width = Math.max( page_width, viewport_width );
	mask_height = Math.max( viewport_height, page_height );	
	};
// track viewport and page size	
function _resize(){ 
	viewport_width = $window.width();
	viewport_height = $window.height();	
	body_width = $body.width("").width();
	body_height = $body.height("").height();
	$.ux("blackbox.size");
	};
// track scroll amounts
function _scroll(){ 
	scroll_y = scroll_top - ( scroll_top = $window.scrollTop() );
	scroll_x = scroll_left - ( scroll_left = $window.scrollLeft() );
	$.ux("blackbox.align");
	};
$(function(){ // attach global handlers when the dom is ready    
	$body = $( document.body );
	$window = $( window ).bind( "resize", _resize ).bind( "scroll", _scroll ); 
	_scroll();
	}); // dom.ready	

})( jQuery ); // secure the $ jQuery alias